ACP implementation bringing 31 CLI tools into t3code#2684
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| async function extractArchive( | ||
| archivePath: string, | ||
| archiveKind: ArchiveKind, | ||
| installRoot: string, | ||
| cmd: string, | ||
| ): Promise<void> { | ||
| await fsPromises.mkdir(installRoot, { recursive: true }); | ||
| switch (archiveKind) { | ||
| case "tar-gz": | ||
| runBlocking("tar", ["-xzf", archivePath], installRoot); | ||
| break; | ||
| case "tar-bz2": | ||
| runBlocking("tar", ["-xjf", archivePath], installRoot); | ||
| break; | ||
| case "tar": | ||
| runBlocking("tar", ["-xf", archivePath], installRoot); | ||
| break; | ||
| case "zip": | ||
| runBlocking("unzip", ["-q", "-o", archivePath, "-d", installRoot], installRoot); | ||
| break; | ||
| case "raw": | ||
| // Treat the downloaded file as the executable itself. | ||
| await fsPromises.copyFile(archivePath, resolveCmdPath(installRoot, cmd)); | ||
| break; | ||
| } | ||
| } |
There was a problem hiding this comment.
🟡 Medium acpRegistry/installer.ts:152
In the "raw" archive case, fsPromises.copyFile throws ENOENT when cmd contains subdirectories like ./bin/agent. The mkdir(installRoot) on line 158 only creates the install root, not the nested parent directories needed for the destination path returned by resolveCmdPath. Consider ensuring the destination directory exists before copying, or using recursive directory creation for the full target path.
case "raw":
// Treat the downloaded file as the executable itself.
- await fsPromises.copyFile(archivePath, resolveCmdPath(installRoot, cmd));
+ {
+ const destPath = resolveCmdPath(installRoot, cmd);
+ await fsPromises.mkdir(path.dirname(destPath), { recursive: true });
+ await fsPromises.copyFile(archivePath, destPath);
+ }
break;🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/acpRegistry/installer.ts around lines 152-177:
In the `"raw"` archive case, `fsPromises.copyFile` throws `ENOENT` when `cmd` contains subdirectories like `./bin/agent`. The `mkdir(installRoot)` on line 158 only creates the install root, not the nested parent directories needed for the destination path returned by `resolveCmdPath`. Consider ensuring the destination directory exists before copying, or using recursive directory creation for the full target path.
Evidence trail:
apps/server/src/acpRegistry/installer.ts lines 92-99 (resolveCmdPath - shows cmd like `./bin/agent.exe` resolves to `${installRoot}/bin/agent.exe`), lines 152-158 (extractArchive - mkdir only creates installRoot), lines 172-173 (raw case - copyFile to resolveCmdPath result without ensuring parent dirs exist), lines 237-251 (caller - archiveKind and cmd are independent inputs from registry data)
| # ACP Registry | ||
|
|
||
| The ACP Registry is the catalog of coding agents that speak the | ||
| [X.com DMs](https://x.com/i/chat/116227293-786375418685165568). T3 Code bundles a |
There was a problem hiding this comment.
🟠 High providers/acp-registry.md:4
The link on line 4 points to a private X.com DM conversation (https://x.com/i/chat/...) instead of the Agent Client Protocol specification. Readers clicking the link will hit a 404 or access-denied page, and the phrase "speak the X.com DMs" is grammatically incomplete. Consider linking to https://agentclientprotocol.com (or the canonical spec URL) and updating the text to "speak the Agent Client Protocol".
| [X.com DMs](https://x.com/i/chat/116227293-786375418685165568). T3 Code bundles a | |
| +[Agent Client Protocol](https://agentclientprotocol.com). T3 Code bundles a |
🤖 Copy this AI Prompt to have your agent fix this:
In file docs/providers/acp-registry.md around line 4:
The link on line 4 points to a private X.com DM conversation (`https://x.com/i/chat/...`) instead of the Agent Client Protocol specification. Readers clicking the link will hit a 404 or access-denied page, and the phrase "speak the X.com DMs" is grammatically incomplete. Consider linking to `https://agentclientprotocol.com` (or the canonical spec URL) and updating the text to "speak the [Agent Client Protocol](...)".
085f518 to
ee86ab9
Compare
| } | ||
| } | ||
|
|
||
| async function installBinary( |
There was a problem hiding this comment.
🟡 Medium acpRegistry/installer.ts:131
In the "raw" archive case, archivePath is always ${installRoot}/agent.bin, and resolveCmdPath(installRoot, target.cmd) also resolves to ${installRoot}/agent.bin when cmd is "agent.bin" or "./agent.bin". The extractArchive function then attempts to copy the file onto itself, which truncates it to 0 bytes on some filesystems. Consider detecting this self-copy case and skipping the copy when the source and destination are identical.
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/acpRegistry/installer.ts around line 131:
In the "raw" archive case, `archivePath` is always `${installRoot}/agent.bin`, and `resolveCmdPath(installRoot, target.cmd)` also resolves to `${installRoot}/agent.bin` when `cmd` is `"agent.bin"` or `"./agent.bin"`. The `extractArchive` function then attempts to copy the file onto itself, which truncates it to 0 bytes on some filesystems. Consider detecting this self-copy case and skipping the copy when the source and destination are identical.
Evidence trail:
apps/server/src/acpRegistry/installer.ts lines 52 (`ARCHIVE_FILENAME` raw → 'agent.bin'), 141 (archivePath construction), 146 (extractArchive call), 177-179 (resolveCmdPath implementation), 207-209 (raw case does copyFile from archivePath to resolveCmdPath(installRoot, cmd)). When cmd='agent.bin', source and dest are both `${installRoot}/agent.bin`.
6587060 to
6964259
Compare
Bundles a snapshot of the Agent Client Protocol registry and exposes a Zed-style installer at Settings → ACP Registry. Users can browse, search, install, and remove 31 ACP-conforming coding agents (everything upstream except the four overlapping with first-party drivers: claude-acp, cursor, opencode, codex-acp). - Bundled snapshot in packages/contracts/src/registry/ refreshed by a new bun run sync:acp-registry script. - Installer in apps/server/src/acpRegistry/ supports binary (download + extract + chmod), npx (bunx), and uvx distribution channels with per-platform target resolution. - Install state persists to ServerSettings.acpRegistryInstalls; cache lives under <baseDir>/acp-agents/<id>/<version>/. - Three new RPC methods (acpRegistry.list/install/uninstall) wired through WsRpcGroup, LocalApi, and WsRpcClient. - See docs/providers/acp-registry.md for the user-facing guide. A generic, data-driven ACP provider adapter lets every installed registry agent appear in the chat provider picker: - makeAcpRegistryDriver builds one ProviderDriver per registry entry; builtInDrivers maps the bundled snapshot into BUILT_IN_DRIVERS so adding agents to the registry needs no new code. - AcpRegistryAdapter spawns the installed distribution over ACP and bridges sessions into the provider runtime. Advertises fs.readTextFile and fs.writeTextFile client capabilities and registers handlers that perform real I/O — many agents (Gemini CLI, etc.) route edits through these methods, and without them writes silently no-opped. - handleReadTextFile returns empty content for a not-yet-created file instead of erroring, so agents proceed to fs/write_text_file rather than aborting when the read precedes the wrie. - resolveAcpPermissionOutcome echoes the agent's advertised optionId back (matched by standard PermissionOptionKind) rather than hardcoding ids like "allow-once". Agents such as Gemini define their own ids (proceed_once / proceed_always / cancel) and rejected hardcoded values with invalid_enum_value, silently aborting edits. - Registry agents are conversation-only in v1 — commit-message / PR / branch / title generation fails fast so callers fall back to first-party providers. - Each registry driver exposes one representative model so the chat model picker has something to select and Send becomes enabled; ACP agents own model selection internally (sessionModelSwitch unsupported). Installing from the registry now auto-creates a default provider instance so the agent shows up in the chat picker without forcing the user through the Add Provider Instance wizard. A one-shot backfill at service startup catches installs that pre-date this logic. Manual instances and customized config are left alone. ACP icon assets use fill="currentColor", but loading them through <img> isolates the SVG from the page's CSS so currentColor resolved to black and stayed invisible in dark mode. A new shared AcpRegistryIcon renders each SVG as a CSS mask-image on a span colored bg-current text-foreground, so icons follow the theme: near-black in light mode, near-white in dark. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6964259 to
9126d46
Compare
The description lives in your X.com DMs.